﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System.Collections.Generic;
using System.Text;

using Microsoft.TeamFoundation.WorkItemTracking.Client;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Logging;
using ScrumTable.DL.Data.Generic.Cache;
using ScrumTable.DL.Data.TFS.Bulk.Properties;
using ScrumTable.DL.Data.TFS.Services.TeamFoundation;

using WorkItemBucket = ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>;
using IterationBucket = ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheIteration>;

using WorkItemSavePair = ScrumTable.DL.Data.Generic.Cache.CacheSavePair<ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>>;
using IterationSavePair = ScrumTable.DL.Data.Generic.Cache.CacheSavePair<ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheIteration>>;

#endregion

namespace ScrumTable.DL.Data.TFS.Bulk.Services
{
    /// <summary>
    /// This class is used to provide error handling facilities for all
    /// TFS data synchronization points.
    /// </summary>
    internal class SaveTraceErrorProvider
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly SaveTraceEntry _traceEntry;
        private readonly WorkItem _wiToTrace;
        private readonly CacheElementBase _changedElement;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new SaveTraceErrorProvider instance, no public constructor
        /// needed.
        /// </summary>
        internal SaveTraceErrorProvider(WorkItem wiToTrace, CacheElementBase changedElement)
        {
            PreCondition.AssertNotNull(wiToTrace, "wiToTrace");
            PreCondition.AssertNotNull(changedElement, "changedElement");

            _traceEntry = new SaveTraceEntry(wiToTrace, changedElement);
            _wiToTrace = wiToTrace;
            _changedElement = changedElement;
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        internal void DumpSaveErrorData(ICollection<Field> invalidFields)
        {
            const string dumpSeparator = "\n{0}:\t\t{1}\t\t=> {2}{3}";
            StringBuilder dump = new StringBuilder();

            foreach (ICacheTrackedDictionaryBucket changedValueBucket in _changedElement.ChangedValues)
            {
                if (_wiToTrace.Fields.Contains(changedValueBucket.Name)) // the TFS WorkItem must contain a field with the given name
                {
                    dump.AppendFormat(
                        dumpSeparator,
                        changedValueBucket.Name,
                        _traceEntry.Values.TryGetValue(changedValueBucket.Name),
                        DumpSaveErrorValue(changedValueBucket),
                        GetDumpErrorFields(invalidFields));
                }
            }
            Logger<DataSaveProcess>.InfoFormat(
                Resources.LogDataSvMngrDumpError,
                _wiToTrace.Id,
                _traceEntry.Revision,
                _changedElement.Revision,
                dump.ToString());
        }

        private string DumpSaveErrorValue(ICacheTrackedDictionaryBucket changedValueBucket)
        {
            return GetDumpErrorValue(changedValueBucket);
        }

        private string GetDumpErrorValue(ICacheTrackedDictionaryBucket changedValueBucket)
        {
            return (changedValueBucket.Value ?? "[null]").ToString();
        }

        private string GetDumpErrorFields(ICollection<Field> invalidFields)
        {
            const string dumpField = "\nInvalid fields:\t\t{0}";

            return (invalidFields.Count > 0) ?  string.Format(dumpField, invalidFields.Join(f => f.Name, () => "; ")) : string.Empty;
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion



        //--------------------------------------------------------------------
        // Nested Classes
        //--------------------------------------------------------------------

        /// <summary>
        /// Contains a structure which should provide additional information about
        /// the data store procedure. This information may be used for debugging
        /// purposes.
        /// </summary>
        private class SaveTraceEntry
        {
            #region Properties
            //--------------------------------------------------------------------
            // Properties
            //--------------------------------------------------------------------

            internal int Revision { get; private set; }
            internal IDictionary<string, object> Values { get; private set; }

            #endregion

            #region Constructors / Destructor
            //--------------------------------------------------------------------
            // Constructors / Destructor
            //--------------------------------------------------------------------

            internal SaveTraceEntry(WorkItem toTrace, CacheElementBase changedElement)
            {
                Values = new Dictionary<string, object>();
                Revision = toTrace.Revision;

                InitializeValues(toTrace, changedElement);
            }

            #endregion

            #region Properties
            //--------------------------------------------------------------------
            // Methods
            //--------------------------------------------------------------------

            private void InitializeValues(WorkItem toTrace, CacheElementBase changedElement)
            {
                // the names of the ICacheTrackedDictionaryBucket correspond to the BackEndId of the CacheFieldDefinitions -> see InitialDataCacheLoader.LoadValues()
                foreach (ICacheTrackedDictionaryBucket changedValueBucket in changedElement.ChangedValues)
                {
                    Values[changedValueBucket.Name] = toTrace.TryGetValue<object>(changedValueBucket.Name, null);
                }
            }

            #endregion
        }
    }
}

